/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include <assert.h>
#include <limits.h>
#include <front/text.h>
#include <front/mem.h>
#include <front/SymTab.h>

#include "type_scope.h"
#include "cfront_y.h"
#include "spectype.h"

extern int cfront_char; /* From cfront_y.c */
extern int my_cfront_lex ();
extern int my_cfront_leng;
extern char *my_cfront_text;

#define AFTER_PARSE (!is_parsing)
/* #define AFTER_PARSE (cfront_root!=NULL) */

static Scope cur_scope = NULL;
static List_declaration_specifiers decl_spec_stack = NULL;

void push_scope (Bool decl_scope)
{
  Scope old = cur_scope;
  if (AFTER_PARSE) return;
  if (decl_scope) cur_scope = Create_StructDefScope (cur_scope, NULL);
  else            cur_scope = Create_cfrontScope (cur_scope, NULL);
  Scope__self(cur_scope) = Scope_Num_Stores(old);
}

Scope get_scope (void)
{ return cur_scope;
}

void set_scope (Scope s)
{ 
  if (AFTER_PARSE) return;
  cur_scope = s;
}

void restore_scope (save_scope s)
{ 
  if (AFTER_PARSE) return;
  cur_scope = save_scope_sc(s);
}

void pop_scope (void)
{ 
   if (AFTER_PARSE) return;
   /* Don't pop it here, set it to the previous value. */
   cur_scope = cfront_Scope_next (cur_scope);
}

/****** Stack of type-specifiers *********/

static struct type_id_list {
  Bool type_id;
  Bool par_list_seen;
  struct type_id_list *next;
} *type_id_stack = NULL;

#define   type_id_list_type_id(x)       ((x)->type_id)
#define   type_id_list_par_list_seen(x) ((x)->par_list_seen)
#define   type_id_list_next(x)          ((x)->next)

static struct type_id_list *Create_type_id_list (Bool t, Bool p, struct type_id_list *n)
{ struct type_id_list *x = fe_malloc (sizeof(*x));
  type_id_list_type_id(x) = t;
  type_id_list_par_list_seen(x) = p;
  type_id_list_next(x) = n;
  return x;
}

void open_decl_specs (void)
{ Bool cur_token_is_typeid = FALSE;

  if (AFTER_PARSE) return;

  switch (cfront_char) {
    case structsym:
    case unionsym:
    case enumsym:
    case signedsym:
    case unsignedsym:
      cur_token_is_typeid = TRUE;
      break;
    case Type_identsym: {
      Type t;
      Bind b = RetrieveBind (cfront_lval.Identsym_type, cur_scope,
                             Scope_Num_Stores(cur_scope), cfront_TypeNameSpace, 0);
      if (b==NULL || Bind_object(b) == NULL) break;
      t = Bind_object(b);
      cur_token_is_typeid = Type_tag(t) == IdType_kind && IdType_std(t);
      break;
    }
  }
  type_id_stack = Create_type_id_list (cur_token_is_typeid, FALSE, type_id_stack);
}

void close_decl_specs (Bool par_list)
{
  if (AFTER_PARSE) return;
  type_id_stack = type_id_list_next (type_id_stack);
  if (type_id_stack == NULL) return;
  type_id_list_par_list_seen(type_id_stack) = par_list;
}

void set_type_id (void)
{
  if (AFTER_PARSE) return;
  type_id_list_type_id(type_id_stack) = TRUE; 
  type_id_list_par_list_seen(type_id_stack) = FALSE;
}

void push_decl_specs (declaration_specifiers l)
{
  if (AFTER_PARSE) return;
  decl_spec_stack = Create_List_declaration_specifiers (l, decl_spec_stack);
}

void pop_decl_specs (declaration_specifiers l)
{
  if (AFTER_PARSE) return;
  if (l==NULL) return;
  decl_spec_stack = List_declaration_specifiers_next (decl_spec_stack);
#if 0
  close_decl_specs();
#endif
}

/* This functions implements the rules in ANSI 3.5.2, which basically states
   that typedef ids and other type specifiers should not be mixed
*/
Bool is_type_def (Ident id)
{ 
  Bool td;
  Bind b;
  
  if (AFTER_PARSE) {
    b = RetrieveBind (id, cur_scope, Scope_Num_Stores(cur_scope),
                      cfront_DefaultNameSpace, Bind_declarator_kind);
    td = b != NULL && Bind_object(b) != NULL &&  is_typedef (Bind_object(b));
    return td;
  }
  
  b = RetrieveBind (id, cur_scope, Scope_Num_Stores(cur_scope),
                    cfront_TypeNameSpace, 0);
  td = b != NULL && Bind_object(b) != NULL;
  
  if (td) {
    Type t = base_type (Bind_object(b));
    assert (Type_tag(t) == IdType_kind);
    if (IdType_std(t)) return TRUE;
    if (type_id_stack != NULL && 
        type_id_list_type_id(type_id_stack) && 
        !type_id_list_par_list_seen(type_id_stack)) return FALSE;
  }
  
  return td;
}

Type declare_id (Ident id)
{
  List_declaration_specifier m;
  Bind b;
  declaration_specifier d;
  
  if (decl_spec_stack == NULL) return NULL;
  if (cur_scope && Scope_tag (cur_scope) == StructDefScope_kind) return NULL;
  
  for (m = declaration_specifiers_specs(List_declaration_specifiers_data(decl_spec_stack));
       m;
       m = List_declaration_specifier_next(m)) {
    d = List_declaration_specifier_data(m);
    if (declaration_specifier_tag(d) == StorageClass_kind &&
        StorageClass_sc(d) == Typedef) {
      b = StoreBind (id, cur_scope, 0,
                     Create_IdType(no_srcinfo, INT_MIN, INT_MIN, id, NULL),
                     cfront_TypeNameSpace, FALSE, FALSE, TRUE);
      return Bind_object(b);
    }    
  }
  /* It is another declaration than a typedef. We need to hide type-defs
     in higher scopes, so we declare it for the current scope.
  */
  b = RetrieveBind (id, cur_scope, Scope_Num_Stores(cur_scope), cfront_TypeNameSpace, 0);
  if (b != NULL) {
    if (Bind_scope(b) == cur_scope) {
      /* We have already have a declaration in the current scope */
      return NULL;
    }
    /* We have a declaration in a higher scope, hide it. */
    StoreBind (id, cur_scope, 0, NULL, cfront_TypeNameSpace,
               FALSE, FALSE, TRUE);
  }
  return NULL;
}

#if 0
void declare_type (Ident id)
{ Store_Bind (id, cur_scope, 0, Create_IdType(no_srcinfo, INT_MIN, id), cfront_TypeNameSpace, FALSE, FALSE);
}
#endif

/**********/
